/**
1、下载全局http-server
2、开启 htpt-server
3、 安装vetur vue vscode snippets  
4、vue的设计思想
    1、设计思想
        1、数据驱动应用
        2、mvvm模式的践行者
    2、vue和model、和视图的关系
        1、用来展示下一个vue程序它应该所处的角色和它的作用，它的设计思想是什么，要完成什么任务
        2、中间这个绿色的大盒子，这个就是vue核心要完成的思想，我们的所有功能都封装在里面，viewdom
        3、vue最重要的功能就是完成viewdom这个功能，它还顺带把vue这一层也给实现了
    3、数据驱动
        1、mvvm三个要素，数据驱动是不是要实现响应式，改数据页面就会变化，这叫数据驱动
        2、那么界面怎么知道发生变化，一定是知道数据发生了变化，这就是数据响应式
    4、模板引擎呢，
        1、vue怎么知道视图里面的东西呢，因为我刚才写代码的时候，明明写的是这个表达式，怎么做的，怎么把这个值替换上
        2、这就涉及到模板引擎的解析了。
    5、渲染的问题，
        1、vue是怎么把这个模板转换成HTML的，工作的时候，这段模板实际上会被vue转换成一个js函数我们称之为渲染函数
        2、那么将来数据发生变化的时候，我们会重新执行渲染函数，得到最新的结果。得到的结果就是传说中的虚拟dom
        3、那么我们要做的事情就是把虚拟dom再变成一个真实的dom，这样的一个过程，它就是一个vue的基本工作原理
    6、vue核心思想
        1、data里的值，来源于服务器，绑定到视图上，视图改变，反作用于model，中间不经过dom操作
        2、data里面有一个数据，这就是model，它现在是写死的值，将来有可能来自服务器，作为vue，是不关心它的来源，我只是用来修改它
        3、它变了，我要改界面，界面变了，要影响它，我要对它反作用
        4、vue的核心作用，中间层，连接我们的数据，以及我们的视图，视图就是我们大家看到的这一层，HTML
        5、中间这一层，它可以把这个数据通过大家看到的响应式来进行数据绑定，data bindings,绑定到视图中，
        6、data 里的msg  html中的{{msg}}
        7、将来呢，如果这个数据发生变化，定时器发生变化，更新它，界面中用户有可能有一些事件，比如我点击事件，又或者说其它的一些事件
        8、总之，这些事件又可以反作用于，让用户可以修改数据模型
        9、所以呢，大家来看，一正一反，通过中间层vue这个中间层，做一个桥梁，避免用户直接接触dom,这样大幅提高用户的开发效率
        10、这就是vue它的核心思想
5、列表渲染
    1、用v-for指令来实现，使用item in items，这种形式来表示我要遍历的这个具体的数据，然后我拿出每个单个的数据从而方便我展示
    2、数据，
6、表单输入绑定
    1、我们再界面中如何处理用户的一些输入行为，form表单的一些输入项的绑定，这里要了解的是双向绑定，自然而然是和数据打交道的
    2、所以叫model，那么通常情况下，v-vmoel是和input textarea select 这些元素来进行绑定。
    3、它决定了两件事，第一呢，就是用户输入的这个值，我们是不是可以把它反作用到数据模型，当数据模型发生变化，这些选项是不是
    4、能反过来自动去修改它的当前项显示用户输入的状态，这就是所谓的双向数据绑定
    5、表单输入和事件是绑定的两个内容，
7、事件处理
    1、可以使用v-on指令来做事件监听， 那么同学们可以监听一些dom事件，这些事件的名字（click）就会作为v-on的参数被传递，
    2、然后后面是它的回调函数。回调函数声明再methods中，就是标准的使用方式

8、模板语法
    1、vue.js使用了基于html的模板语法的方式来声明页面的样式。同时能够把vue data中声明的这些数据能够动态的展示再页面中
    2、插值文本
    3、使用v-bind这个指令，然后呢给它传递一个参数，这个参数会在冒号后面去指定，等号后面是它具体的值，大家把动态的变量
    4、放在里面去，一旦你使用v-bind指令，这个东西将会变成动态的值，也就是双引号里面，看起来像静态的，它其实绑定的是data里面一个值
    5、如果要去页面内容去绑定一个普通的文本的插值或者是绑定一个属性值
9、样式class绑定
    1、用户会有一个这么一个选中的效果
    2、:class={},对象的形式，还有数组，现在想要动态的指定一个对象，这个对象的key决定了将来哪个类的名称就是这个active
    3、那么这个active要不要加呢，取决于它后面的表达式到底是true还是false，
    4、selectedCourse，如果这个值和我当前遍历的这个值是同一个，则我认为它应该加上active，
10、神奇的模板语法
    1、vue会把编写的这些模板转换成渲染函数，那么这个渲染函数将来的目标是要为了能够生成虚拟dom,所以我们叫虚拟dom的渲染函数
    2、结合响应系统，vue能够智能的计算出最少的需要重新渲染多少组件，并把dom操作次数减少到最少。
    3、我们再次执行渲染函数，得到一个全新的这样一个虚拟dom,那么在新旧dom之间做这个比对，是不是就可以知道真正的dom操作
    4、这个过程看起来很复杂，
    5、赋值购物车，代码，当它结束它这个初始化，这些过程之后，我们在这个选项里面，app.$options里面，可以拿到我们编译器
    6、生成的这个渲染函数。这就是刚才给大家介绍到的这个虚拟dom渲染函数。
    7、把#app内部删除掉，写一个render(){},把渲染dom函数放进去，和原先效果一样。
11、计算属性和监听器
    1、计算属性
        1、在实际工作中，如果模板中出现了很复杂的这样的计算表达式，一般情况下你都需要计算属性的方式，把它写到下面的配置项
        2、计算属性有缓存，如果值没有发生变化，则页面不会重新渲染
        3、为什么不把total写到methods，就是因为这个缓存性，这个值写到methods，不管这个值也没有变化，每次都会重新计算
        4、只要这个值没有变，那么total的值永远都是哪个值，他也不会重新渲染
    2、监听器
        1、默认情况下watch初始化不执行，我想让它立刻执行，把immediate设置为true，这个表示立即执行一次
        2、下面还有个选项叫做deep，观察深层次对象的变化
    3、到底使用哪一个
        1、从简洁性上看，如果能使用computed实现，大家就用computed，因为这种方式它具有缓存性
        2、而watch有立即执行问题，而且还要额外的声明属性，要不然我们就没有办法完成它的功能
        3、如果使用选项的方式，看起来过于复杂了，
        4、语境上说，如果一个值变化了，我可以做一些事情，是不是不止这一个事情，影响多个值
        5、而计算属性，如果有一个值，是由其它值得来的，一旦这些值发生变换，不管是其中的任何一个值，我都要变。
        6、适合多个值影响一个值的情形。在实际工作中，一般都是其它值得变化导致一个最新值得产生
        7、不适合用wathc
            1、firstname变了，我要把这两个值拼成fullname，
            2、lastnaem变了，我也要变，特别啰嗦，是不是就不太适合，
            3、但是什么情形适合，如果firstname变了，我会影响这个值，也会影响那个值，没有这个lastname这个情形，
            4、一个值会影响其它多个值，这个时候用它。
            5、还有就是大家如果在做一些异步操作，或者开销很大得这些操作得时候，大家就适合用监听器了
        8、什么时候适合用watch
            1、这是一个问答程序，用户在输入框里输入问题，如果我发现用户得问题发生变化了
            2、我要去远程得服务器上去请求一下，这个问题得最新得答案，大家可以想一下，这个是异步得请求，非常得消耗资源
            3、那如果用户在这个input里面，疯狂得进行敲击，每一次键盘得输入都会发送一个请求，一个非常可怕得请求风暴
            4、是不是就爆发了，很有可能把我们得程序就卡死了
            5、这个时候就为了避免请求风暴，一般情况下，我们会做一个防抖，会做一个debounce,
            6、引入了ladash这个库里得debouce方法，
12、生命周期
    1、概念
        每个vue在创建得时候都要经过一系列得初始化过程，例如，需要设置数据监听，编译模板，将实例挂载到dom，并在数据变化时
        更新dom，称为vue实例得生命周期
    2、使用生命周期得钩子
        在vue实例得生命周期过程中会运行一些叫做生命周期钩子得函数，这给用户在不同阶段添加自己代码得机会。
    3、模拟接口
        1、数据已经初始化了，但是我们什么时候调用这个接口去访问呢，在实际开发过程中，有两个生命周期得钩子比较常用
        2、一个叫组件得创建钩子，还有一个钩子叫做mounted，
        3、created 组件实例已创建，虽然没有挂载，它自己得数据，以及老爹传下来得数据都可以放心大胆得去访问
        4、由于没有挂载，这些数据还没有转换成真实得dom，dom元素不存在
        5、mounted，将我们前面写得所有得渲染函数执行之后得到得虚拟dom转换成真实dom，也就是说大家在mounted中可以访问到真实得dom元素了
        6、你不要认为这个时间点早，你看到得这个界面可能会早，没有哪个区别呀，实际上我们访问数据花销的时间远远大于它初始化挂载的时候，所以没有实质上的区别
        7、new vue的时候，创建了当前组件的根实例，创建了整个应用程序的根实例，然后呢，是一系列子组件，子组件里面这些子的组件，子的实例都会去创建
        8、首先要做的事情是什么呢，是一些数据的准备，像生命周期，比如我这个生命周期主要是一些这些特别属性的创建
        9、以后我们会跟大家深入的介绍，像老爹的引用，$parent孩子的引用$children，一些跟生命攸关的，那你出生了，肯定得你得知道你爹是谁，但是你还不知道你孩子是谁
        10、但是这些变量，它们得初始化已经做好了，事件得监听，等等
        11、那么接下来是beforecreate得钩子，所以这个时间点是非常早得，以至于开发中很少用到它
        12、再然后是初始化，注入，校验，就是一系列数据得传入，再这个阶段呢，我们会有从来自父组件注入得一些特别得数据，或者是父辈，祖辈传过来得，我们还可以做一些
        13、相关得校验工作，比如说这些数据也没有冲突呀，跟我当前得data，或者property也没有冲突呀，最后，这些数据全部准备就绪了。才会created
        14、接下来会执行挂载逻辑，这是内部去执行得，大家不用关心
        15、我们找到这个宿主了，然后我把当前这些数据结合着我写得模板变成dom,就算完成了
        16、完成之后，就要执行挂载，就是到了dom得追加操作了，我希望把我得内容追加到这个宿主元素上。比如说咱们在界面中写得这个#demo,
        17、在我执行一系列得这个转换过程之前，我会先before mount，通知一下大家，我准备去干这个事了，但是现在还没有去做转换
        18、但是，当我把这些事情都做完之后，大家可以看到，vm.$el,也就是这个dom,元素实际上已经转换完成了，它转换完成之后，会触发这个mounted，会通知用户，
        19、说现在可以访问dom,你可以放心大胆得更新dom，访问dom,也就是说到这，挂载是咱们挂载是完毕得，初始化流程结束。这是第一阶段。
        20、第二阶段，就是这个无限得循环，只要这个组件，它一天不死，不消失，不被删除掉，那么它就处在这个循环过程，不停得在监控咱们这个数据得更新，一旦数据发生更新
        21、准备做更新之前，先beforeupdate,那么等我把所有得更新操作做完，dom操作更新完，我再通知用户，updated
        22、所以，你要再更新之前先记录一下，这个状态，你要beforeupdate，反之，你要看到更新之后得结果，你要再updated这个钩子里去做事情
        23、那么用户手动得掉destroy，当前得组件实例，这个时候也不是立刻销毁，销毁之前，再给大家一点机会，你还可以做一些事情，在这你还可以访问组件实例
        24、比如说，把程序中得定时器取消，销毁子组件，以及事件监听器，等等一些可能造成内存泄漏得东西，把它先解除掉
        25、那么真正销毁完毕得事件是destroyed,
        26、我们在用组件得时候，涉及到组件得缓存，keep-alive,是否销毁它，那么这个时候呢，
13、组件
    1、概念
        1、组件是可复用得vue实例，带有一个名字，我们可以在一个通过new vue创建得vue得根实例中，把这个组件作为自定义元素来使用
    2、定义组件，
        1、父组件传递属性，子组件自定义事件，父组件监听
        2、coutse-list，这个组件它不关心数据来源，它只关心怎么去显示，所以在这里面用属性得方式来给它传递一个courses这么一个属性
        3、这个变量它本身还是在我们当前根组件去维护，然后我们只需要把它作为参数传递给这个course-list ,将来让它在里面去使用
        4、父子之间最简单得沟通是通过属性得方式来传递值给它去显示
        5、那么下一步我们很可能会遇到一些特别得交互得情况，比如说我得子组件可能要跟我得父组件产生一些数据通信，我发生了一些变化，我要告诉你，这时候父组件做一些
        6、相应得变化响应，那么自定义组件自定义事件以及它得监听就是这种方式得一个实现方式
        7、我们假设，这个东西将来明显得要做什么事情呢，在它得内部，用户会交互，比如说会拍个回车，点个新增，那这个时候，我们是不是会通过某种方式，告诉老爹要去新增
        8、一个东西，并且把这个新增得数据给我们传递出来，那它传递得数据，老爹必须知道，这就涉及到了自定义事件的派发和监听过程了
    3、自定义组件双向绑定
        1、course这个变量我确实不想在子组件中维护，它可能和其它各种各样的数据都是在父组件中一起维护的，这时候，我只是想把变量传给你，交给你去维护
        2、但是最终要使用的那个人还是这个父组件本身，v-model="course",由我自己来维护这个course，但是修改却是里面的人，这个组件本身
        3、自定义组件默认是不支持的，首先要明确一件事情，语法糖v-model最终会转换成 :value="course" @input="oninput",
        4、同时，（语法糖v-model）它还表示一个属性的传递，叫value的传递，这个回调函数里面具体的逻辑实际上是这样的，它希望内部派发的这个事件传递的就是那个最新的要变更的值
        5、然后呢，我在这里做成一个赋值，其实我做的事情是，@input="course=$event",$event是固定的名字，表示传递出来的参数，这个参数我把赋值给当前的course变量
        6、所以v-model="course" 最终会变成 :value="course" @input="course=$event",我们就可以知道在我们在course-add的内部，我们要做的事情，其实第一就是
        7、我们要派发一个默认的input事件，并且要把最新的值传递出来作为参数，这是我在内部要做的事情
        8、我在course-add这个内部呢，要接收一个属性叫value，这个value实际上是我course-add用来去持有的这么一个值
        9、但是我不去改变它，我只是派发事件，怎么去改变，怎么赋值，由父组件来决定
        10、让组件本身变得无状态话， 这个是组件定义得过程中大家应该注意得这个事情
14、插槽
    1、概念
        1、对话框得内容显然我将来在用得时候，是在父组件中去指定得，但是这块内容，我将来是要把它显示到message组件得内部得，这个就是内容分发
        2、我现在要在外面提供内容，但是我要分发到里面指定得位置，这就是内容分发技术
        3、vue中得内容分发是插槽，这个名字非常得形象，它有点像电脑上得插槽，usb得接口，我流出这个位置，我得内容是不确定得，但是由你来决定得
        4、你把你得内容通过这个插槽传递给我就可以了，这个就是它得概念，
    2、麻烦的内容分发
        1、 @click="$emit('close',false)加上一个自定义组件得派发，于是乎这里要做得事情是,派发一个自定义事件，告诉老爹，要传递一个值，这个值就是把这个变成false，
        2、直接写表达式，不需要再这里method，要传一个事件，这个事件就叫close事件，然后再把这个值传出去，false，把show这个变量变成false，
        3、不希望说把传进来得这个值，混淆了，到底谁在改，只能老爹改，依照单项数据流得原则，所以我现在只是派发一个事件，让老爹去改，把这个值告诉它。
        4、现在这个编程模式就显得很啰嗦，我在派发，老爹这边呢，还要去处理， @close="show=$event"
    3、利用syc简洁的内容分发
        1、语法上显得很啰嗦，sync修饰符，:show.sync="show" 这表示将来会把它展开成一个事件，这个事件叫做 @update:show=""
        2、就是将来要传出来得那个参数我要赋值给show这个变量， @click="$emit('update:show',false)",子组件自定义事件就规定死了。
    4、匿名插槽
        1、因为在这个message里面，我们传入的内容是非常固定的，就是默认内容，但是如果将来，在这message有多部分内容，不止这个默认内容，
    5、具名插槽
        1、这个时候就需要具名插槽，把指定的内容放到指定的位置
        2、这里首先要通过template方式，来区分出来它的内容的名字，声明一下，这个具名之前的默认内容，就放在没有名字的template
        3、现在我还需要一个标题，那我再来一个，怎么区分这两个template，肯定要有一个名字，用v-slot：
        4、它是通过参数的形式来指定名字，指令的参数用冒号来指定，title，代表将来那个插槽里面的名称 <template v-slot:title></template>
        5、默认的名字，其实是一个语法糖，它其实也有v-slot定义，它的名字叫做default，
        6、在子组件中，<slot name="title"></slot>,将来这块就会被替换成外面的那个template，当然我可以在这里设置默认的内容，
        7、比如说写个默认标题，将来用户没有指定，则用这个，如果指定了，则把这个替换掉
    6、作用域插槽，其实就是在插槽上加一个上下文
        1、在父组件这里头，我定义的内容其实都是由父组件的作用域来决定的，包括这里面的恭喜二字
        2、比如说我现在在这里加上一个变量，叫title，获取的场所应该是获取当前message的这个父组件中去查找的
        3、也就是说这个title大家伙把它定义的位置是，这个根实例里面这个title
        4、那现在有一个特殊的需求，这个值呢，它不是来自当前的上下文，而是来自我们message的子组件，那应当如何获取这个值呢
        5、这个使用方式就是作用域插槽了，我们取而代之应该怎么做呢，
        6、v-slot:title="slotProps",在这里赋一个值
        7、这个值叫临时的上下文，你随便起名 
        8、然后你要做的事情是，在子组件中去指定这个值，那么这是一个上下文，同学们可以在可以直接通过上下文访问这个slotProps.title，
        9、<slot name="title" title="来自子组件的title">默认标题</slot>
        10、上下文就切换了，取决于使用场景，就是你这个值到底是来自内部还是外部，决定了你要不要用作用域插槽
15、组件化实践
    1、优点
        可以增加代码的复用性，可维护性，和可测性
    2、使用场景：
        通用组件，实现最基本的功能，按钮组件，输入框组键，布局组件
        业务组件，完成具体的业务，具有一定的复用性，例如登录组件，轮播图
        页面组件，组织应用各部分独立内容，需要时在不同页面组件间切换，例如列表页，详情页
    3、如何使用组件
        1、定义：vue.component(),components 选项
        2、分类：有状态组件，functional abstract
        3、通信：props $emit()/$on(), provide/inject,$children/$parent/$root/$attrs/$listeners
        4、内容分发<slot>,<template>,v-slot
        5、使用及优化：is,keep-alive,异步组件
    4、组件的本质
        1、vue中的组件经历如下过程
            1、组件配置=》vuecomponent实例=》render()=》virtual DOM=>dom
            2、所以组件的本质是产生虚拟的dom
        2、过程详解
            1、组件大家平常写的只是配置，只是对象
            2、但是这个过程中vue内部到底做了什么，是中间这三部，大家是看不到的
            3、因为你写了配置，最终呢希望看见dom,但是实际上vue内部替你做了三件事
            4、首先它把你写的组件配置转化成了vuecomponent实例，构造函数
            5、这个构造函数之后，又通过方式把它new出来，变成实例，实例里面会有render方法，
            6、render方法怎么来的，前面给大家讲过，你写的模板会转换成render方法，
            7、render方法在合适的时间去执行获得虚拟dom
            8、虚拟dom又通过更新的过程转换成真实dom
            9、所以隐藏在内部的这一部分是vue替你做的。
            10、你应当知道这个过程，最后你应该知道组件的本质就是产生虚拟dom
16、必会api，数据相关
    1、foo:{foo:'foo'},后面又添加了一个属性，foo:{foo:'foo',bar:'bar'}
    2、通过编程的方式给这个foo追加了一个另外一个属性叫做bar,你给它放了一个bar属性，然后你希望它能在界面中动态的变更
    3、显示出来，并且能够在以后变化过程中也会动态的变更，但是你会发现一个奇怪的现象，那么能够动态变更响应式的
    4、其实只有一开始你在声明的这个foo属性，后来在动态添加的这个bar属性，是没有办法动态响应式的
    5、这个其实是和vue去处理数据初始化过程中，这个数据处理方式是又关系的，
    6、如果后续动态的在里面追加一个属性，并确保这个属性同样是响应式的，这个时候请你用vue.set这个api来做
    7、那下面通过一个例子来展示Vue.set这个api的具体使用方式
        1、 this.courses.forEach((c) => {
                    c.price=this.price;
                });
        2、然后输入框里输入100,发现不再好用了。因为这个新添加的价格它不是响应式的，纵使你改了这个值，纵使现在这个数据已经是100
        3、但是界面中却没有更新，要么你在这里输出一下，要门在控制台打印一下，要门你点一下，你会发现，因为我点的时候，是不是会改咱们的样式
        4、这个样式会让界面重新刷新，于是它会按照最新的值去计算，最新的值其实就是100，现在的化，我们怎么解决这个问题
        5、我们batchUpdate的时候，这里要使用官方提供的Vue.set(c,'price',this.price)
    8、Vue.delete
        1、我们平常delete obj['propetry']你这样删除，最终界面中是不会响应的
        2、Vue.delete(c,'price')
        3、只不过没有第三个参数
    9、实例方法，这两个同名实例方法是别名，是刚才这两个方法的别名



 */